<template>
  <demo-content :title="title">
    <p>
      在绝大多数情况下， 你会将一个或多个 canvas 元素与其他 HTML
      控件结合起来使用，以便让用户可以通过输入数值或其他方式来控制应用程序。
    </p>
    <p>
      要将其他 HTML 控件与 canvas 结合起来使用，首先想到的办法可能是将控件嵌入到
      canvas 元素之中。不过这么做并不行，因为任何放入 canvas
      元素整体部分的东西，只有在浏览器不支持 canvas 元素时才会被显示出来。
    </p>
    <p>
      为了让 HTML 控件看上去好像出现在 canvas 范围内，可以使用 CSS
      将这些控件放置在 canvas 之上。
    </p>
    <p>
      下面的例子提供了一个用于启动或停止动画效果的控件，这个存在于 DIV
      元素之中的控件是半透明的，并且浮动在 canvas 之上。我们将这种
      DIV叫做玻璃窗格(glass pane)，因为它看起来像是一个浮动在 canvas
      元素之上的玻璃板。
    </p>
    <p>
      <strong style="color: red"
        >CSS
        规范书中规定：采用绝对定位方式的元素将被绘制在相对定位方式的元素之上。</strong
      >
    </p>
    <p>
      Canvas 规范中说，应该首先考虑使用内置的 HTML 控件，而非使用 Canvas API
      从头实现控件。这通常是个好的建议，想要用 Canvas API
      来编写全新的控件，一般来说会涉及大量的工作。
    </p>
    <div style="position: relative">
      <div id="glasspane">
        <h3>Bouncing Balls</h3>
        <p>One hundred balls bouncing</p>
        <span
          id="controller"
          @click="trigger"
          :style="{ color: isStop ? 'greenyellow' : 'red' }"
          >{{ isStop ? "Start" : "Stop" }}</span
        >
      </div>
      <canvas ref="canvasRef"></canvas>
    </div>
  </demo-content>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
import _ from "lodash";
import { drawGrid, arc } from "./utils";
const title = "画布中用HTML元素";

// 指定组件上的属性
defineOptions({ title });

const canvasRef = ref<HTMLCanvasElement>();

class Ball {
  private _x: number;
  private _y: number;
  private _xV: number;
  private _yV: number;
  private _r: number;
  private _color: string;
  private _canvas: HTMLCanvasElement;
  constructor(private ctx: CanvasRenderingContext2D) {
    this._canvas = ctx.canvas;
    this._x = 125;
    this._y = 125;
    this._xV = _.random(5, true);
    this._yV = _.random(5, true);
    this._r = Math.random() * 25 + 5;
    const f = () => _.random(255, false);
    this._color = `rgba(${f()}, ${f()}, ${f()}, ${_.random(0.7, true) + 0.3})`;
  }
  move() {
    this.updatePos();
    arc.draw(this.ctx, {
      x: this._x,
      y: this._y,
      radius: this._r,
      fillStyle: this._color,
    });
  }
  private updatePos() {
    if (this._x + this._xV + this._r > this._canvas.width) {
      this._xV *= -1;
      this._x = this._canvas.width - this._r;
    } else if (this._x + this._xV - this._r < 0) {
      this._xV *= -1;
      this._x = this._r;
    } else {
      this._x += this._xV;
    }
    if (this._y + this._yV + this._r > this._canvas.height) {
      this._yV *= -1;
      this._y = this._canvas.height - this._r;
    } else if (this._yV + this._y - this._r < 0) {
      this._yV *= -1;
      this._y = this._r;
    } else {
      this._y += this._yV;
    }
  }
}
const isStop = ref(true);

function trigger() {
  isStop.value = !isStop.value;
}

onMounted(() => {
  const canvas = canvasRef.value!;

  canvas.width = 800;
  canvas.height = 600;
  const ctx = canvas.getContext("2d")!;
  const balls: Ball[] = [];
  drawGrid(ctx, { stepx: 25, stepy: 25 });
  for (let i = 0; i < 100; ++i) {
    balls.push(new Ball(ctx));
  }
  setInterval(() => {
    if (isStop.value) return;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawGrid(ctx, { stepx: 25, stepy: 25 });
    balls.forEach((ball) => ball.move());
  }, 1000 / 60);
});
</script>

<style src="./default.css" scoped></style>
<style>
#glasspane {
  position: absolute;
  left: 50px;
  top: 25px;
  background-color: #a0a0a0a0;
  padding: 0 8px;
  border: 1px solid black;
  border-radius: 5px;
  font-family: Droid Sans, Arial, Helvetica, sans-serif;
}

#glasspane h3 {
  color: yellow;
  cursor: default;
}

#glasspane p {
  color: blue;
}

#glasspane #controller {
  font-size: 42px;
  cursor: pointer;
}
</style>
